package com.shawn.integeration.log.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.shawn.integeration.log.annonation.Log;
import com.shawn.integeration.log.aspect.LogAspect;
import com.shawn.integeration.log.enums.BusinessStatus;
import com.shawn.integeration.log.model.SysOperLog;
import com.shawn.integeration.log.util.IpUtils;
import com.shawn.integeration.log.util.ServletUtils;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;

/**
 * 日志处理服务
 *
 * @author: shawn
 * @Date: 2023/12/26 11:46
 * @Version 1.0.0
 */
@Service
public class LogHandleService {
    private Logger logger = LoggerFactory.getLogger(LogHandleService.class);

    public void handle(JoinPoint jp, Log log, Exception e, Object jsonRes) {
        // 1. 获取客户端IP和访问接口地址
        SysOperLog operLog = new SysOperLog();
        operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
        operLog.setOperIp(IpUtils.getIpAddr());
        operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());

        // 2. 根据实际情况获取用户信息
        // operLog.setOperName(loginUser.getUsername());

        // 3. 若是存在异常,则记录异常信息
        if (ObjectUtil.isNotNull(e)) {
            operLog.setStatus(BusinessStatus.FAIL.ordinal());
            operLog.setErrorMsg(StrUtil.sub(e.getMessage(), 0, 2000));
        }

        // 4.设置方法名称
        String className = jp.getTarget().getClass().getName();
        String methodName = jp.getSignature().getName();
        operLog.setMethod(className + "." + methodName + "()");
        // 设置请求方式
        operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
        // 处理设置注解上的参数
        getControllerMethodDescription(jp, log, operLog, jsonRes);
        // 设置消耗时间
        operLog.setCostTime(DateUtil.current() - LogAspect.TIME_THREADLOCAL.get());
        logger.info(String.format("operLog : [%s]", JSONUtil.toJsonStr(operLog)));
    }

    public void getControllerMethodDescription(JoinPoint jp, Log log, SysOperLog operLog, Object jsonRes) {
        // 设置 action 动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存 request，参数和值
        if (log.isSaveReqData()) {
            operLog.setOperParam(getReqValue(jp, operLog.getMethod()));
        }
        // 是否需要保存 response，参数和值
        if (log.isSaveResData() && Objects.nonNull(jsonRes)) {
            String trimRes = StrUtil.sub(JSONUtil.toJsonStr(jsonRes), 0, 2000);
            operLog.setJsonResult(trimRes);
        }
    }

    private String getReqValue(JoinPoint joinPoint, String reqMethodName) {
        Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
        boolean isParamEmpty = MapUtil.isEmpty(paramsMap);
        boolean isPutOrPost = HttpMethod.PUT.name().equals(reqMethodName) ||
                HttpMethod.POST.name().equals(reqMethodName);
        String params;
        if (isParamEmpty && isPutOrPost) {
            params = argsArrayToString(joinPoint.getArgs());
        } else {
            params = JSONUtil.toJsonStr(paramsMap);
        }
        return StrUtil.sub(params, 0, 2000);
    }

    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (ArrayUtil.isEmpty(paramsArray)) {
            return params;
        }
        for (Object o : paramsArray) {
            if (Objects.nonNull(o) && !isFilterObject(o)) {
                try {
                    params += JSONUtil.toJsonStr(o) + " ";
                } catch (Exception e) {
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            // 如果是数组类型且数组元素的类型是 MultipartFile 的子类或子接口，则需要过滤
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            // 如果是集合类型，且存在 MultipartFile元素，如果是则返回 true
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            // 如果是Map类型，且存在MultipartFile 的元素值,则返回 true。
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest
                || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
